#include "stdafx.h"
#include "ResourceManager.h"
#include "ResourceLoader.h"
#include "Resource.h"
#include "SDLTexture.h"
#include "SpriteSheet.h"
#include "Font.h"

#include "XMLParsers.h"

using namespace std;

namespace ZeEngine
{
    ResourceManager::ResourceManager()
    {
    }

    ResourceManager::~ResourceManager()
    {
    }

    void ResourceManager::AddResourceToMap(XMLNodeList& groups, const string& strResource, const std::string& strResourceFile)
    {          
        for (auto it = groups.begin(); it != groups.end(); ++it)
        {
            const XMLNode* pGroupNode = (*it);

            if (pGroupNode)
            {
                const string groupName = pGroupNode->GetAttribute<string>("name");

                if (groupName.length() > 0)
                {
                    for (auto jt = pGroupNode->GetChildren().begin(); jt != pGroupNode->GetChildren().end(); ++ jt)
                    {
                        const XMLNode* pResourceNode = (*jt).second;

                        if (pResourceNode)
                        {
                            const string resourceType = pResourceNode->GetName();
                            const string resourceName = pResourceNode->GetAttribute<string>("name");
                            const string resourceFile = pResourceNode->GetAttribute<string>("file");

                            if (resourceType == strResource)
                            {
                                //Make sure the resource doesnt already exist
                                if (GetResource<Resource>(resourceName, groupName) == NULL)
                                {
                                    if (resourceType.length() > 0 && resourceName.length() > 0 && resourceFile.length() > 0)
                                    {
                                        if (resourceType == "Texture")
                                        {
                                            CreateResource<SDLTexture>(resourceFile, resourceName, groupName);
                                        }
                                        else if (resourceType == "SpriteSheet")
                                        {
                                            const string resourceTexture = pResourceNode->GetAttribute<string>("texture");

                                            if (resourceTexture.length() > 0)
                                            {
                                                SpriteSheet* pSpriteSheet = CreateResource<SpriteSheet>(resourceFile, resourceName, groupName);

                                                if (pSpriteSheet)                                                
                                                {

                                                    SDLTexture* pTexture = GetResource<SDLTexture>(resourceTexture, groupName);

                                                    if (pTexture)
                                                    {
                                                        pSpriteSheet->SetTexture(GetResource<SDLTexture>(resourceTexture, groupName));
                                                    }
                                                    else
                                                    {
                                                        LOG_ERROR("Error parsing resource file: " + strResourceFile + " SpriteSheet: " + resourceName + " in group: " + groupName + " could not find texture: " + resourceTexture);
                                                    }
                                                }
                                            }
                                            else
                                            {
                                                LOG_ERROR("Error parsing resource file: " + strResourceFile + " SpriteSheet: " + resourceName + " in group: " + groupName + " does not have a texture");
                                            }
                                        }
                                        else if (resourceType == "SpriteSheetAnimations")
                                        {                                      
                                            const string spriteSheet = pResourceNode->GetAttribute<string>("spritesheet");

                                            if (spriteSheet.length() > 0)
                                            {
                                                SpriteSheet* pSpriteSheet = GetResource<SpriteSheet>(spriteSheet, groupName);

                                                if (pSpriteSheet)
                                                {
                                                    pSpriteSheet->AddAnimations(resourceName, resourceFile);
                                                }
                                                else
                                                {
                                                    LOG_ERROR("Cannot find " + spriteSheet + " in resource file " + strResourceFile + " for Sprite Sheet Animation " + resourceName + " in group " + groupName);
                                                }                                              
                                            }
                                            else
                                            {
                                                LOG_ERROR("Error parsing resource file: " + strResourceFile + " SpriteSheet: " + resourceName + " in group: " + groupName + " does not have a texture");
                                            }
                                        }
                                        else if (resourceType == "Font")
                                        {
                                            CreateResource<Font>(resourceFile, resourceName, groupName);
                                        }
                                    }
                                    else
                                    {
                                        LOG_ERROR("Error parsing resource file: " + strResourceFile + " Error in group " + groupName);
                                    }
                                }
                                else
                                {
                                     LOG_ERROR("Duplicate resource in resource file: " + strResourceFile + " with name: " + resourceName + " in group: " + groupName);
                                }
                            }
                            else
                            {    
                                //If we dont know the type then log it
                                if (resourceType != "Texture" && 
                                    resourceType != "SpriteSheet" &&
                                    resourceType != "SpriteSheetAnimations" &&
                                    resourceType != "Font")
                                {
                                    LOG_ERROR("Error parsing resource file: " + strResourceFile + " Unknown type: " + resourceType + " in group: " + groupName + " with name: " + resourceName);
                                }
                            }
                        }
                    }
                }
                else
                {
                    LOG_ERROR("Error parsing resource file: " + strResourceFile + ". Invalid group name");
                }
            }
        }
    }

    void ResourceManager::AddResourceFile(const std::string& strResourceFile)
    {
        XMLParsers parser(strResourceFile);

        if (parser.IsLoaded())
        {
            XMLNodeList groups = parser.GetNodeList("Resources.Group");

            //Load the resources in the correct order, for example sprite sheets need textures so they are loaded after           
            AddResourceToMap(groups, "Texture", strResourceFile);
            AddResourceToMap(groups, "Font", strResourceFile);
            AddResourceToMap(groups, "SpriteSheet", strResourceFile);
            AddResourceToMap(groups, "SpriteSheetAnimations", strResourceFile);
        }
        else
        {
            LOG_ERROR("Could not load resource file: " + strResourceFile);
        }
    }
 
    void ResourceManager::LoadResourceGroup(const std::string& strGroup)
    {
        if (m_pResourceLoader)
        {
            auto it = m_resources.find(strGroup);

            if (it != m_resources.end())
            {
                for (auto jt = it->second->begin(); jt != it->second->end(); ++jt)
                {
                    Resource* pResource = jt->second;

                    if (pResource)
                    {
                        pResource->Load(*m_pResourceLoader);
                    }
                }
            }
        }
    }

    void ResourceManager::UnloadResourceGroup(const std::string& strGroup)
    {
        if (m_pResourceLoader)
        {
            auto it = m_resources.find(strGroup);

            if (it != m_resources.end())
            {
                for (auto jt = it->second->begin(); jt != it->second->end(); ++jt)
                {
                    Resource* pResource = jt->second;

                    if (pResource)
                    {
                        pResource->Unload(*m_pResourceLoader);
                    }
                }
            }
        }
    }

    map<std::string, Resource*>* ResourceManager::CreateGroupInMap(const std::string& strGroup)
    {
        ResourceMap* pMap = NULL;

        auto it = m_resources.find(strGroup);

        if (it == m_resources.end())
        {
            pMap = new ResourceMap();

            if (pMap)
            {
                m_resources.insert(ResourceGroupPair(strGroup, pMap));
            }
        }
        else
        {
            pMap = it->second;
        }

        return pMap;
    }

    void ResourceManager::InsertResourceInMap(const std::string& strName, const std::string& strGroup, Resource* pResource)
    {
        ResourceMap* pMap = CreateGroupInMap(strGroup);

        if (pMap)
        {
            pMap->insert(ResourceMapPair(strName, pResource));
        }
    }

    void ResourceManager::SetResourceLoader(std::unique_ptr<ResourceLoader> resourceLoader)
    {
        m_pResourceLoader = move(resourceLoader);
    }
}